package eu.catengine.renderable.text;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import javax.microedition.khronos.opengles.GL10;

import eu.catengine.graphic.Font;
import eu.catengine.graphic.TextureRegion;
import eu.catengine.primitive.Point;
import eu.catengine.primitive.Size;
import eu.catengine.primitive.Vector;
import eu.catengine.renderable.shape.Shape;

/**
 * Klasa przechowująca dane dotyczące tekstu wyświetlanego w scenie. <br>
 * <br>
 * Punt zaczepienia obiektu (anchor point) znajduje się w lewym górnym rogu.
 * 
 * @author mbienkowski
 */
public class Text extends Shape
{
    private Font mFont;
    private String mText;

    private Point mOriginPoint;

    private int mNewLines;
    private char mCurrentChar;
    private String mCurrentString;

    private TextureRegion mTextureRegion;
    /**
     * Tworzy tekst wyświetlany w scenie.
     * 
     * @param text
     *            <code>String</code> Wyświetlany tekst.
     * @param point
     *            <code>Point</code> Punkt zaczepienia.
     * @param font
     *            <code>Font</code> Czcionka pobrana z FontManager.
     */
    public Text(String text, Point point, Font font)
    {
	setTextData(text, point.x, point.y, font);
    }

    /**
     * Tworzy tekst wyświetlany w scenie.
     * 
     * @param text
     *            <code>String</code> Wyświetlany tekst.
     * @param x
     *            <code>float</code> Współrzędna X punktu zaczepienia.
     * @param y
     *            <code>float</code> Współrzędna Y punktu zaczepienia.
     * @param font
     *            <code>Font</code> Czcionka pobrana z FontManager.
     */
    public Text(String text, float x, float y, Font font)
    {
	setTextData(text, x, y, font);
    }

    private void setTextData(String text, float x, float y, Font font)
    {
	mText = text;
	mFont = font;
	mOriginPoint = new Point(x, y);

	this.setName("");
	this.mSize = new Size(mFont.getTextWidth(text), mFont.getCharHeight());
	this.mVertices = new float[] { x, y - mFont.getCellHeight(), // V1 -
								     // bottom
								     // left
		x, y, // V2 - top left
		x + mFont.getCellWidth(), y - mFont.getCellHeight(), // V3 -
								     // bottom
								     // right
		x + mFont.getCellWidth(), y, // V4 - top right
	};
	this.mVerticesCount = this.mVertices.length / 2;
	ByteBuffer byteBuffer = ByteBuffer.allocateDirect(this.mVertices.length * 4);
	byteBuffer.order(ByteOrder.nativeOrder());
	this.mVertexBuffer = byteBuffer.asFloatBuffer();
	this.mVertexBuffer.put(this.mVertices);
	this.mVertexBuffer.position(0);
    }

    @Override
    public void draw(GL10 gl)
    {
	if (mVisible == false)
	    return;

	mNewLines = 0;
	super.moveTo(mOriginPoint);

	gl.glPushMatrix();
	gl.glEnable(GL10.GL_TEXTURE_2D);
	gl.glEnable(GL10.GL_BLEND);
	gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
	gl.glEnable(GL10.GL_ALPHA_BITS);
	gl.glColor4f(mColor.red, mColor.green, mColor.blue, mColor.alpha);
	gl.glBindTexture(GL10.GL_TEXTURE_2D, mFont.getTexturePointer());
	gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
	gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
	gl.glFrontFace(GL10.GL_CW);

	for (int i = 0; i < mText.length(); i++)
	{
	    mCurrentChar = mText.charAt(i);

	    if (mCurrentChar == '\n')
	    {
		mNewLines++;
		super.moveTo(new Point(mOriginPoint).add(0, -mNewLines * mFont.getCharHeight() * getScaleY()));
		continue;
	    }

	    mCurrentString = mText.substring(i, i + 1);
	    mTextureRegion = mFont.getTextureRegion(mCurrentString);

	    if (mTextureRegion == null)
	    {
		mCurrentChar = '#';
		mTextureRegion = mFont.getTextureRegion("#");
	    }

	    gl.glVertexPointer(2, GL10.GL_FLOAT, 0, mVertexBuffer);
	    gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureRegion.getTextureBuffer());
	    gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, mVerticesCount);

	    super.moveBy(mFont.getCharWidth(mCurrentChar) * getScaleX(), 0);
	}

	gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
	gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
	gl.glDisable(GL10.GL_ALPHA_BITS);
	gl.glDisable(GL10.GL_TEXTURE_2D);
	gl.glPopMatrix();
    }

    @Override
    public void update(long elapsedTime)
    {
	// TODO Auto-generated method stub
    }

    // Extra class method

    /**
     * Ustawia wartość tekstu.
     * 
     * @param text
     *            <code>String</code> Nowa wartość tekstu.
     */
    public void setText(String text)
    {
	mText = text;
	
	this.mSize = new Size(mFont.getTextWidth(text), mFont.getCharHeight());
    }

    /**
     * Dodaje tekst na koniec.
     * 
     * @param text
     *            <code>String</code> Dodawany tekst.
     */
    public void addText(String text)
    {
	mText += text;
	
	this.mSize = new Size(mFont.getTextWidth(text), mFont.getCharHeight());
    }

    /**
     * Dodaje tekst na początek.
     * 
     * @param text
     *            <code>String</code> Dodawany tekst.
     */
    public void addTextBefore(String text)
    {
	mText = text + mText;
	
	this.mSize = new Size(mFont.getTextWidth(text), mFont.getCharHeight());
    }
    
    @Override
    public void moveBy(float x, float y)
    {
	mOriginPoint.x += x; 
	mOriginPoint.y += y;
	
        super.moveBy(x, y);
    }
    
    @Override
    public void moveBy(Vector vector)
    {
	mOriginPoint.x += vector.x; 
	mOriginPoint.y += vector.y;
	
        super.moveBy(vector);
    }
    
    @Override
    public void moveTo(float x, float y)
    {
	mOriginPoint.x = x; 
	mOriginPoint.y = y;
	
        super.moveTo(x, y);
    }
    
    @Override
    public void moveTo(Point point)
    {
	mOriginPoint.x = point.x; 
	mOriginPoint.y = point.y;
	
        super.moveTo(point);
    }

    /**
     * Zwraca tekst.
     * 
     * @return <code>String</code> Tekst obiektu.
     */
    public String getText()
    {
	return mText;
    }
}
